package semantica;

import java.util.Enumeration;
import java.util.Hashtable;
import semantica.tipos.Tipo;
import semantica.tipos.TipoArray;
import semantica.tipos.TipoIdentificador;
import semantica.tipos.TipoNatural;
import semantica.tipos.TipoTupla;
import semantica.tipos.*;


/**
 *
 * @author Javi
 */
public class TablaDeSimbolos {


            private class ContenidoTablaDeSimbolos{
                 private String id;
                 private String clase; 
                 private String valor;
                 private String nivel;
                 private Tipo tipo;
                 private int dir;

                 public ContenidoTablaDeSimbolos(String id, String clase, String nivel, Tipo tipo, int dir){
                     this.id = id;
                     this.clase = clase; 
                     this.nivel = nivel;
                     this.tipo = tipo;
                     this.dir = dir;
                     this.valor = "";
                 }

                             public String getId() {
                                     return id;
                             }
                             public void setId(String id) {
                                     this.id = id;
                             }
                             public String getClase() {
                                     return clase;
                             }
                             public void setClase(String clase) {
                                     this.clase = clase;
                             }
                             public String getNivel() {
                                     return nivel;
                             }
                             public void setNivel(String nivel) {
                                     this.nivel = nivel;
                             }
                             public Tipo getTipo() {
                                     return tipo;
                             }
                             public void setTipo(Tipo tipo) {
                                     this.tipo = tipo;
                             }
                             public int getDir() {
                                     return dir;
                             }
                             public void setDir(int dir) {
                                     this.dir = dir;
                             }
                             public String getValor() {
                                     return valor;
                             }
                             public void setValor(String valor) {
                                     this.valor = valor;
                             }
             }
        
    private Hashtable <String, ContenidoTablaDeSimbolos> ts;
    private Hashtable <String, ContenidoTablaDeSimbolos> tsl;

    
    
    public TablaDeSimbolos(){
        ts = new Hashtable <String, ContenidoTablaDeSimbolos>();
    }
    
    public TablaDeSimbolos(TablaDeSimbolos tabla){
       this.ts = new Hashtable <String, ContenidoTablaDeSimbolos>();
       this.ts = (Hashtable <String, ContenidoTablaDeSimbolos>)tabla.getTs().clone();
       if (tabla.tsl != null) this.tsl = (Hashtable <String, ContenidoTablaDeSimbolos>)tabla.getTsl().clone();
       //if (this.tsl != null) this.tsl = (Hashtable <String, ContenidoTablaDeSimbolos>)tabla.getTsl().clone();
    }
    
    
    public void anadeTS(String id, String clase, Tipo tipo, int dir){
        if (tsl != null ) // significa que estamos en subprogramas porque existe
            tsl.put(id,new ContenidoTablaDeSimbolos(id, clase, "local", tipo, dir));
        else // si no, añadimos a la local
            ts.put(id,new ContenidoTablaDeSimbolos(id, clase, "global", tipo, dir));
    } 
    
    public void eliminaTSLocal(){
       this.tsl = null;
    } 
    
    public void creaTSLocal(){
       this.tsl = new Hashtable <String, ContenidoTablaDeSimbolos>();
    } 

    
    public int getDir(String id){
       if (tsl != null) return getDirLocal(id);
       else return getDirGlobal(id);
    }
    
        /* Miramos en la local y si no en la global
         */
        private int getDirLocal(String id) {
            if (tsl.containsKey(id)) return tsl.get(id).getDir();
            else return getDirGlobal(id);
        }

        private int getDirGlobal(String id) {    
            if (ts.containsKey(id))  return ts.get(id).getDir();
            return 0;
        }
    
    public Tipo getTipo(String id){
       if (tsl != null) return getTipoLocal(id);
       else return getTipoGlobal(id);
    }
    
        private Tipo getTipoLocal(String id){
            if (tsl.containsKey(id)) return tsl.get(id).getTipo();
            else return getTipoGlobal(id);
        }

        private Tipo getTipoGlobal(String id){
            if (ts.containsKey(id)) return ts.get(id).getTipo();
            return new TipoError();
        }
    
    
    public boolean existeId(String id){
        if (tsl != null) return existeIdLocal(id);
        else return existeIdGlobal(id);
    }
    
        public boolean existeIdGlobal(String id){
            return ts.containsKey(id);
        }

        /*Si no esta en local miro en global*/
        private boolean existeIdLocal(String id){
            if (tsl.containsKey(id)) return true;
            else return existeIdGlobal(id);
        }
        
        /*Si no esta en local miro en global*/
        public boolean existeIdSoloLocal(String id){
            return (tsl.containsKey(id));
        }
        
    public String dameNivel(String id){
        if (tsl != null) return dameNivelLocal(id);
        else return dameNivelGlobal(id);
    }
    
        private String dameNivelGlobal(String id){
            if  (ts.containsKey(id))
                return ts.get(id).getNivel();
            else 
                return "";
        }

        /*Si no esta en local miro en global*/
        private String dameNivelLocal(String id){
            if (tsl.containsKey(id)) return tsl.get(id).getNivel();
            else return dameNivelGlobal(id);
        }
        
        public String dameClase(String id){
        if (tsl != null) return dameClaseLocal(id);
        else return dameClaseGlobal(id);
    }
    
        private String dameClaseGlobal(String id){
            if  (ts.containsKey(id))
                return ts.get(id).getClase();
            else 
                return "";
        }

        /*Si no esta en local miro en global*/
        private String dameClaseLocal(String id){
            if (tsl.containsKey(id)) return tsl.get(id).getClase();
            else return dameClaseGlobal(id);
        }
          
    
    public boolean esConst(String id){
       if (tsl != null) return esConstLocal(id);
       else return esConstGlobal(id);
    }
    
        private boolean esConstGlobal(String id){
           if (ts.containsKey(id)) return ts.get(id).getClase().equals("const");
           return false;
        }

        private boolean esConstLocal(String id){
           if (tsl.containsKey(id)) return tsl.get(id).getClase().equals("const");
           else return esConstGlobal(id);
        }
    
    public boolean esNatural(String id){
       if (tsl != null) return esNaturalLocal(id);
       else return esNaturalGlobal(id);
    }
    
        private boolean esNaturalGlobal(String id){
           if (ts.containsKey(id)) return ts.get(id).getTipo().getTipo().equals("natural");
           return false;
        }

        private boolean esNaturalLocal(String id){
           if (tsl.containsKey(id)) return tsl.get(id).getTipo().getTipo().equals("natural");
           else return esNaturalGlobal(id);
        }
    
    public char getValorCharacter(String id){
        if (ts.containsKey(id))
           if (ts.get(id).tipo.getTipo().equals("character")) return ((TipoCharacter) ts.get(id).tipo).getValor();
        return 0;
    }
    
        private char getValorCharacterLocal(String id){
            if (ts.containsKey(id))
               if (ts.get(id).tipo.getTipo().equals("character")) return ((TipoCharacter) ts.get(id).tipo).getValor();
            return 0;
        }

        private char getValorCharacterGlobal(String id){
            if (ts.containsKey(id))
               if (ts.get(id).tipo.getTipo().equals("character")) return ((TipoCharacter) ts.get(id).tipo).getValor();
            return 0;
        }
    
    public int getValorInteger(String id){
        if (tsl != null) return getValorIntegerLocal(id);
        else return getValorIntegerGlobal(id);
    }
    
        private int getValorIntegerGlobal(String id){
            if (ts.containsKey(id))
               if (ts.get(id).tipo.getTipo().equals("integer")) return ((TipoInteger) ts.get(id).tipo).getValor();
            return 0;
        }

        private int getValorIntegerLocal(String id){
            if (tsl.containsKey(id))
               if (tsl.get(id).tipo.getTipo().equals("integer")) return ((TipoInteger) tsl.get(id).tipo).getValor();
            return getValorIntegerGlobal(id);
        }
    
    public int getValorNatural(String id){
        if (tsl != null) return getValorNaturalLocal(id);
        else return getValorNaturalGlobal(id);
    }
    
        private int getValorNaturalGlobal(String id){
            if (ts.containsKey(id))
               if (ts.get(id).tipo.getTipo().equals("natural")) return ((TipoNatural) ts.get(id).tipo).getValor();
            return 0;
        }

        private int getValorNaturalLocal(String id){
            if (tsl.containsKey(id))
               if (tsl.get(id).tipo.getTipo().equals("natural")) return ((TipoNatural) tsl.get(id).tipo).getValor();
            return getValorNaturalGlobal(id);
        }
    
    public float getValorFloat(String id){
        if (tsl != null) return getValorFloatLocal(id);
        else return getValorFloatGlobal(id);
    }
    
        private float getValorFloatGlobal(String id){
            if (ts.containsKey(id))
               if (ts.get(id).tipo.getTipo().equals("float")) return ((TipoFloat) ts.get(id).tipo).getValor();
            return 0;
        }

        private float getValorFloatLocal(String id){
            if (tsl.containsKey(id))
               if (tsl.get(id).tipo.getTipo().equals("float")) return ((TipoFloat) tsl.get(id).tipo).getValor();
            return getValorFloatGlobal(id);
        }
    
    public boolean getValorBoolean(String id){
        if (tsl != null) return getValorBooleanLocal(id);
        else return getValorBooleanGlobal(id);
    }
        private boolean getValorBooleanGlobal(String id){
            if (ts.containsKey(id))
               if (ts.get(id).tipo.getTipo().equals("boolean")) return ((TipoBoolean) ts.get(id).tipo).getValor();
            return false;
        }
        
        private boolean getValorBooleanLocal(String id){
            if (tsl.containsKey(id))
               if (tsl.get(id).tipo.getTipo().equals("boolean")) return ((TipoBoolean) tsl.get(id).tipo).getValor();
            return getValorBooleanGlobal(id);
        }
   
    /**
     * PAR DEPURAR FIXME
     */
    public void muestraDatosTipo(){
        
        Enumeration e = ts.elements();
        
        while(e.hasMoreElements()){
            ContenidoTablaDeSimbolos c = (ContenidoTablaDeSimbolos) e.nextElement();
            String id = c.getId();
            String clase = c.getClase();
            Tipo tipo = c.getTipo();
            int tam = c.getTipo().getTam();
            int dir = c.getDir();
            
            String mostrar = "    clase: "  + clase +  ", id: " + id;
            
            if (tipo.getTipo().equals("natural"))
                mostrar += ", valor: " + ((TipoNatural) tipo).getValor() + ", tipo: <t: " + tipo.getTipo();
            else if (tipo.getTipo().equals("integer"))
                 mostrar += ", valor: " + ((TipoInteger) tipo).getValor() + ", tipo: <t: " + tipo.getTipo();
            else if (tipo.getTipo().equals("float"))
                 mostrar += ", valor: " + ((TipoFloat) tipo).getValor() + ", tipo: <t: " + tipo.getTipo();
            else if (tipo.getTipo().equals("character"))
                 mostrar += ", valor: " + ((TipoCharacter) tipo).getValor() + ", tipo: <t: " + tipo.getTipo();
            else if (tipo.getTipo().equals("boolean"))
                 mostrar += ", valor: " + ((TipoBoolean) tipo).getValor() + ", tipo: <t: " + tipo.getTipo();
            else if (tipo.getTipo().equals("array")){
                  mostrar += ", valor: <t: " + tipo.getTipo() + ", nelms: " + ((TipoArray)tipo).getNumElem()
                          + ", tbase: ";
                  Tipo tb = ((TipoArray) tipo).getTipoBase();
                  if (tb.getTipo().equals("ref"))
                      mostrar += "< t: ref " + ", id: " + ((TipoIdentificador)tb).getId() + " >";
                  else if (tb.getTipo().equals("array"))
                      mostrar += "< t: array , nelems: " + ((TipoArray)tipo).getNumElem() + 
                                  ", tbase: " + ((TipoArray)tb).getTipoBase().getTipo() + " >";
                  else if (tb.getTipo().equals("tupla"))
                    mostrar += " <t: tupla, " + ", campos:" + ((TipoTupla)tb).muestraCampos();  
                  else mostrar += " <t: " + tb.getTipo() ;
            } else if (tipo.getTipo().equals("tupla"))
                  mostrar += " tipo: <t: " + tipo.getTipo() + ", campos:" + ((TipoTupla)tipo).muestraCampos();   
            else if (tipo.getTipo().equals("ref")){
                  mostrar += ", tipo: <t: " + tipo.getTipo() + ", id: " + ((TipoIdentificador)tipo).getId();
               } 
            else if (tipo.getTipo().equals("proc")){
                  mostrar += ", tipo: <t: " + tipo.getTipo() + ", campos: " + ((TipoProc)tipo).muestraCampos();
               }
           
            mostrar += ", tam: " + tam + ">" + ", dir: " + dir;
   
            System.out.println(mostrar);        

        }
    }
    
    
    public Hashtable<String, ContenidoTablaDeSimbolos> getTs() {
        return ts;
    }

    public void setTs(Hashtable<String, ContenidoTablaDeSimbolos> ts) {
        this.ts = ts;
    }
    
    public Hashtable<String, ContenidoTablaDeSimbolos> getTsl() {
        return tsl;
    }

    public void setTsl(Hashtable<String, ContenidoTablaDeSimbolos> tsl) {
        this.tsl = tsl;
    }
    
}
